/**
 * 店铺相关
 */
import { mysql } from 'src-ssr/db/connect';
import TableHelper from 'src-ssr/db/tableHelper';
import * as tools from 'src-ssr/util/common';
import { Sequelize, Op, QueryTypes } from 'sequelize';
import models, { sequelize } from 'src-ssr/db/sequelize';
import moment from 'moment';
import sms from 'src-ssr/util/sms';
import jwt from 'jsonwebtoken';
import autocode from 'src-ssr/util/autocode';
import path from 'path';
import fs from 'fs';
import formidable from 'formidable';
import sharp from 'sharp';
import { v4 as uuidv4 } from 'uuid';
import workflow from 'src-ssr/util/workflow';

// shop鉴权
function authentication (req) {
  return new Promise((resolve, reject) => {
    const token = req.get('conAuth');
    if (!token) {
      console.error('shop鉴权码空');
      resolve(false);
    } else {
      jwt.verify(token, global.privateKey, (err, decoded) => {
        if (err) { // 过期
          console.error('shop鉴权失败', err);
          resolve(false);
        } else {
          const { uid, nickname } = decoded;
          resolve({ uid, nickname });
        }
      });
    }
  });
}

// 店铺信息
export const info = async (req, res) => {
  const { id } = req.query;
  if (!id) {
    return res.json({ result: 'fail', message: '店铺不存在或已关店' });
  }

  // 取店铺信息
  const shop = await models.m_shops.findOne({
    attributes: ['tenant_id', 'shop_name', 'linkman', 'tel', 'short_tel', 'address', 'logo_img', 'intro', 'pv'],
    where: {
      uuid: id,
      state: 1
    },
    raw: true
  });

  if (!shop) {
    return res.json({ result: 'fail', message: '店铺不存在或已关店' });
  }

  if (!shop.logo_img || shop.logo_img.length === 0) {
    shop.logo_img = '/images/default_img.jpg';
  } else {
    shop.logo_img = shop.logo_img[0].image;
  }

  // 更新浏览量
  models.m_shops.update({
    pv: Sequelize.literal('IFNULL(pv, 0) + 1')
  }, {
    where: { uuid: id }
  });

  res.json({
    shop
  });
};

// 店铺货物
export const catalog = async (req, res) => {
  const { id } = req.query;
  if (!id) {
    return res.json({ catalog: [] });
  }

  // 取店铺信息
  const shop = await models.m_shops.findOne({
    attributes: ['tenant_id'],
    where: {
      uuid: id,
      state: 1
    },
    raw: true
  });

  if (!shop) {
    return res.json({ catalog: [] });
  }

  // 取店铺商品分类
  // 根结点
  const root = await models.t_material_catalog.findOne({
    attributes: [[Sequelize.literal('replace(bin_to_uuid(uid, 1), "-", "")'), 'uid'], 'id', 'catalog_name'],
    where: { tenant_id: shop.tenant_id, parent_id: -1 },
    raw: true
  });

  const catalog = await models.t_material_catalog.findAll({
    attributes: [[Sequelize.literal('replace(bin_to_uuid(uid, 1), "-", "")'), 'uid'], 'catalog_name'],
    where: { tenant_id: shop.tenant_id, parent_id: root.id, sell: 1 },
    order: [['sort', 'ASC']],
    raw: true
  });

  delete root.id;

  res.json({
    catalog: [root, ...catalog]
  });
};

// 货物信息
async function fetchData ({ startRow, count, filter, sortBy, descending }) {
  // 取租户信息
  let tenant_id = '';
  if (filter && filter.shopId) {
    const shop = await models.m_shops.findOne({
      attributes: ['tenant_id'],
      where: {
        uuid: filter.shopId,
        state: 1
      },
      raw: true
    });
    if (!shop) return { result: 'fail', message: '店铺不存在' }

    tenant_id = shop.tenant_id;
  } else if (filter && filter.id) {
    const material = await models.t_materials.findOne({
      attributes: ['tenant_id'],
      where: {
        uid: tools.uuidToBin(filter.id),
        sell: 1
      },
      raw: true
    });

    if (!material) return { result: 'fail', message: '货物不存在' }

    tenant_id = material.tenant_id;
  }

  const where = { tenant_id, sell: 1 };

  if (filter) {
    if (filter.id) {
      where.uid = tools.uuidToBin(filter.id);
    }
    if (filter.catalog_id) {
      const catalog = await models.t_material_catalog.findOne({
        attributes: ['id', 'parent_id'],
        where: { tenant_id, uid: tools.uuidToBin(filter.catalog_id) },
        raw: true
      });
      if (catalog && catalog.parent_id > 0) {
        where['catalog_id' + (filter?.level || 1)] = catalog.id;
      }
    }
    if (filter.query) {
      where[Op.or] = [
        { material_name_model: { [Op.like]: '%' + filter.query.trim() + '%' } },
        { barcode: { [Op.like]: '%' + filter.query.trim() + '%' } },
        { brand: { [Op.like]: '%' + filter.query.trim() + '%' } },
        { origin: { [Op.like]: '%' + filter.query.trim() + '%' } },
        { material_no: { [Op.like]: '%' + filter.query.trim() + '%' } },
        { material: { [Op.like]: '%' + filter.query.trim() + '%' } }
      ];
    }
  }

  const order = [['recommend', 'DESC'], ['pv', 'DESC']];
  sortBy && order.push([sortBy, descending ? 'DESC' : 'ASC']);

  let ret = { rows: [], count: 0 };
  try {
    ret = await models.t_materials.findAndCountAll({
      attributes: [[Sequelize.literal('replace(bin_to_uuid(uid, 1), "-", "")'), 'uid'], 'material_no', 'material_name', 'material_model',
        'material', 'material_unit', 'brand', 'origin', 'barcode', 'images', 'sale_price', 'describe', 'pv'],
      where,
      order,
      offset: count ? startRow : undefined,
      limit: count || undefined,
      group: count ? ['t_materials.uid', 't_materials.material_no', 't_materials.material_name',
        't_materials.material_model', 't_materials.material', 't_materials.material_unit',
        't_materials.brand', 't_materials.origin', 't_materials.barcode', 't_materials.images',
        't_materials.sale_price', 't_materials.describe', 't_materials.pv', 't_materials.recommend'] : undefined
    });
  } catch (e) {
    console.error('取店铺货物数据出错', e);
  }

  // 处理其它数据
  const rows = [];
  ret.rows.forEach(row => {
    const jsonRow = row.toJSON();

    // 数量转数值
    for (const key of ['sale_price']) {
      if (jsonRow[key]) jsonRow[key] = Number(jsonRow[key]);
    }

    if (!jsonRow.images || jsonRow.images.length === 0) {
      jsonRow.images = [{ idx: 0, batch: uuidv4(), image: '/images/default_img.jpg', thumb: '/images/default_img.jpg' }];
    }
    for (const img of jsonRow.images) {
      if (!img.batch) {
        img.batch = uuidv4();
      }
    }

    rows.push(jsonRow);
  });

  if (filter && filter.id) {
    if (rows.length > 0) {
      // 取同名其它型号的商品
      const [otherModels] = await mysql.query(`SELECT replace(bin_to_uuid(uid, 1), "-", "") uid, material_model
        FROM t_materials
        WHERE material_name=? AND tenant_id=?
        ORDER BY pv`, [rows[0].material_name, tenant_id]);
      rows[0].otherModels = otherModels;

      return rows[0];
    } else {
      return {};
    }
  } else {
    return {
      data: rows,
      rowsNumber: typeof ret.count === 'object' ? ret.count.length : ret.count
    }
  }
}

// 货物列表
export const list = async (req, res) => {
  const { shop } = req.query;
  const { startRow, count, filter, sortBy, descending } = req.body;
  filter.shopId = shop;

  const ret = await fetchData({ startRow, count, filter, sortBy, descending });
  res.json(ret);
};

// 详细信息
export const detail = async (req, res) => {
  const { id } = req.query;

  const ret = await fetchData({ filter: { id } });

  if (ret.result !== 'fail') {
    // 更新浏览量
    models.t_materials.update({
      pv: Sequelize.literal('IFNULL(pv, 0) + 1')
    }, {
      where: { uid: tools.uuidToBin(id) }
    });

    const [shop] = await mysql.query(`SELECT s.uuid, s.shop_name
      FROM t_materials m
      LEFT JOIN m_shops s ON m.tenant_id=s.tenant_id
      WHERE m.uid=?`, [tools.uuidToBin(id)]);
    ret.shop_id = shop[0].uuid;
    ret.shop_name = shop[0].shop_name;
  }

  res.json(ret);
};

// 发送验证码
export const sendVerCode = async (req, res) => {
  const { phone } = req.body;
  const result = await sms.sendVerCode(phone);
  res.json(result);
};

// 验证验证码
export const checkVerCode = async (req, res) => {
  const { phone, code } = req.body;
  const result = await sms.checkVerCode(phone, code);

  if (result.result === 'ok') { // 新用户注册
    let consumer = await models.m_consumers.findOne({ where: { tel: phone } });
    if (!consumer) {
      // 创建
      consumer = await models.m_consumers.create({
        nickname: phone,
        tel: phone,
        create_ip: tools.getIP(req),
        last_login_time: Date.now() / 1000,
        last_login_ip: tools.getIP(req),
        login_count: 1,
        create_time: Date.now() / 1000
      });
      consumer = await models.m_consumers.findOne({ where: { tel: phone } });
      const token = jwt.sign({
        uid: tools.binToUuid(consumer.uid).replace(/-/g, ''),
        nickname: phone
      },
      global.privateKey,
      { expiresIn: 180 * 24 * 60 * 60 });// token 180天过期
      result.token = token;
    } else {
      if (consumer.state > 0) {
        consumer.last_login_time = Date.now() / 1000;
        consumer.last_login_ip = tools.getIP(req);
        consumer.login_count = consumer.login_count + 1;
        await consumer.save();

        const token = jwt.sign({
          uid: tools.binToUuid(consumer.uid).replace(/-/g, ''),
          nickname: consumer.nickname
        },
        global.privateKey,
        { expiresIn: 180 * 24 * 60 * 60 });// token 180天过期
        result.token = token;
      } else {
        result.result = 'fail';
        result.message = '用户已停用，请联系店家或是管理平台';
      }
    }
  }
  res.json(result);
};

// 获取消费者信息
export const getConsumer = async (req, res) => {
  // 检查jwt
  const auth = await authentication(req);
  if (!auth) {
    return res.status(401).send('unauthorized user');
  }

  const { uid } = auth;
  const consumer = await models.m_consumers.findOne({ where: { uid: tools.uuidToBin(uid) } });
  if (!consumer) {
    return res.json({ result: 'fail', message: '非法的用户ID' });
  }
  if (consumer.state == 0) {
    return res.json({ result: 'fail', message: '用户已停用，请联系店家或是管理平台' });
  }
  const { nickname, fullname, gender, headimg, tel, company, address } = consumer.toJSON();
  res.json({ nickname, fullname, gender, headimg, tel, company, address });
};

// 更新消费者信息
export const setConsumer = async (req, res) => {
  // 检查jwt
  const auth = await authentication(req);
  if (!auth) {
    return res.status(401).send('unauthorized user');
  }

  // 更新
  const { formData } = req.body;
  formData && formData.tel && delete formData.tel; // 清除电话号码

  await models.m_consumers.update(formData, { where: { uid: tools.uuidToBin(auth.uid) } });
  res.json({ result: 'ok' });
};

// 下订单
export const createOrder = async (req, res) => {
  // 检查jwt
  const auth = await authentication(req);
  if (!auth) {
    return res.status(401).send('unauthorized user');
  }

  const { formData } = req.body;
  const { shopId } = formData;
  // 取店铺信息
  const shop = await models.m_shops.findOne({
    attributes: ['tenant_id'],
    where: {
      uuid: shopId
    },
    raw: true
  });

  // 生成编号
  formData.bill_no = await autocode.newCode({ code: 'SL', custom: '店', tid: shop.tenant_id }); // 生成编号
  formData.type = 'shop'; // 默认值
  formData.consumer_uid = tools.uuidToBin(auth.uid);
  formData.tenant_id = shop.tenant_id;
  formData.create_time = Date.now() / 1000;

  if (formData.detail && formData.detail.length > 0) {
    // 明细转json
    if (typeof formData.detail === 'string') {
      formData.detail = JSON.parse(formData.detail);
    }
  }

  for (const row of formData.detail) {
    const mat = await models.t_materials.findOne({
      attributes: ['material_no', 'material_name', 'material_model', 'material_unit', 'brand', 'origin', 'barcode', ['default_image', 'images'], 'sale_price'],
      where: { uid: tools.uuidToBin(row.material_uid) },
      raw: true
    });
    const snapshot = { ...mat, ...row };
    row.material_uid = tools.uuidToBin(row.material_uid);
    row.tenant_id = shop.tenant_id;
    row.tax_rate = 0;
    row.snapshot = snapshot;
  }

  formData.total_money = formData.detail.reduce((a, b) => a + b.amount * b.price, 0);

  try {
    let newModel = await models.t_sales.create(formData);
    newModel = await models.t_sales.findByPk(newModel.id);
    newModel = newModel.toJSON();

    formData.detail.forEach(row => {
      row.master_uid = newModel.uid;
      delete row.id;
    });
    await models.t_sale_details.bulkCreate(formData.detail);
  } catch (e) {
    console.error('创建订单出错', e);
  }

  workflow.broadcast('new_order', `${moment().format('YY/MM/DD HH:mm')} 新订单 ${formData.bill_no} 到达，有 ${formData.detail.length} 笔货物需要配送`, shop.tenant_id);

  res.json({ result: 'ok' });
};

// 订单清单
export const orderList = async (req, res) => {
  // 检查jwt
  const auth = await authentication(req);
  if (!auth) {
    return res.status(401).send('unauthorized user');
  }

  const { startRow, count, filter, sortBy, descending } = req.body;

  const where = {
    type: 'shop',
    consumer_uid: tools.uuidToBin(auth.uid)
  };

  if (filter) {
    if (filter.query) {
      where.uid = {
        [Op.in]: Sequelize.literal(`(
          SELECT m.uid
          FROM t_sales m
          LEFT JOIN t_sale_details d ON m.uid=d.master_uid
          LEFT JOIN t_materials c ON d.material_uid=c.uid
          LEFT JOIN m_shops s ON m.tenant_id=s.tenant_id
          WHERE m.type='shop' AND m.consumer_uid=${mysql.escape(tools.uuidToBin(auth.uid))}
          AND (s.shop_name LIKE ${mysql.escape('%' + filter.query.trim() + '%')}
            OR c.material_name LIKE ${mysql.escape('%' + filter.query.trim() + '%')}
            OR c.material_model LIKE ${mysql.escape('%' + filter.query.trim() + '%')})
        )`)
      };
    }
  }

  let ret = { rows: [], count: 0 };
  try {
    ret = await models.t_sales.findAndCountAll({
      attributes: [[Sequelize.literal('bin_to_uuid(t_sales.uid, 1)'), 'suid'], 'bill_no', 'status',
        'remark', 'consignee', 'tel', 'address', 'total_money', 'tenant_id', 'create_time', 'create_date'],
      include: [{
        model: models.t_sale_details,
        attributes: [[Sequelize.literal('bin_to_uuid(t_sale_details.material_uid, 1)'), 'material_uid'], 'amount', 'price', 'tax_rate', 'money', 'remark', 'snapshot'],
        include: [{
          model: models.t_delivery_details,
          attributes: ['deliver_amount'],
          include: [{
            model: models.t_deliveries,
            attributes: ['bill_no', 'logistics', 'logistics_no', ['create_time', 'delivery_time']]
          }]
        }]
      }],
      where,
      order: [['id', 'DESC']],
      offset: count ? startRow : undefined,
      limit: count || undefined,
      group: count ? ['t_sales.id', 't_sales.uid', 't_sales.bill_no', 't_sales.status',
        't_sales.remark', 't_sales.consignee', 't_sales.tel', 't_sales.address',
        't_sales.total_money', 't_sales.tenant_id'] : undefined
    });
  } catch (e) {
    console.error('采购清单出错', e);
  }

  const rows = [];
  for (const row of ret.rows) {
    const jsonRow = row.toJSON();

    const shop = await models.m_shops.findOne({
      attributes: ['uuid', 'shop_name'],
      where: { tenant_id: row.tenant_id }
    });

    if (shop) {
      jsonRow.shopId = shop.uuid;
      jsonRow.shop_name = shop.shop_name;
    } else {
      jsonRow.shopId = jsonRow.shop_name = '';
    }

    if (jsonRow.t_sale_details) {
      // 商品信息用快照数据
      for (const detail of jsonRow.t_sale_details) {
        for (const key of ['images', 'material_no', 'material_name', 'material_unit', 'material_model']) {
          detail[key] = detail.snapshot[key];
        }
        delete detail.snapshot;

        for (const item of detail.t_delivery_details) {
          tools.flatDetail(['t_delivery'], item); // 拉平信息
          item.delivery_date = moment(item.delivery_time, 'X').format('YYYY/MM/DD HH:mm')
        }

        // 数量转数值
        for (const key of ['amount', 'price', 'tax_rate', 'money']) {
          if (detail[key]) detail[key] = Number(detail[key]);
        }
      }
    }
    // 数量转数值
    for (const key of ['total_money']) {
      if (jsonRow[key]) jsonRow[key] = Number(jsonRow[key]);
    }
    rows.push(jsonRow);
  }

  res.json({
    data: rows,
    rowsNumber: typeof ret.count === 'object' ? ret.count.length : ret.count
  });
};

// 取性别字典
export const getGenderDict = async (req, res) => {
  const dictionary = await tools.getSystemConfig('dictionary', 0); // 数据字典

  res.json({
    gender: dictionary.gender
  });
};

/* 文件上传 */
export const upload = async (req, res) => {
  // 检查jwt
  const auth = await authentication(req);
  if (!auth) {
    return res.status(401).send('unauthorized user');
  }

  // 上传路径：uploads/tid/YYYYMM/

  const dir = process.env.DEV ? '../..' : '..';
  const rootFolder = path.join(path.resolve(__dirname, dir), 'uploads');

  if (!fs.existsSync(rootFolder)) {
    fs.mkdirSync(rootFolder)
  }

  // 当月目录
  const YYYYMM = moment().format('YYYYMM');
  const folder = path.join(rootFolder, 'shop', YYYYMM);
  if (!fs.existsSync(folder)) {
    tools.mkdirs(folder);
  }
  // 缩略图目录
  const thumbFolder = path.join(folder, 'thumb');
  if (!fs.existsSync(thumbFolder)) {
    fs.mkdirSync(thumbFolder)
  }

  const form = formidable({
    uploadDir: folder,
    keepExtensions: true,
    maxFieldsSize: 100 * 1024 * 1024 // 限制上传100M
  });

  form.parse(req, (err, fields, files) => {
    if (err) {
      return res.json({ result: 'fail', message: err });
    }

    const images = [];
    for (const key in files) {
      const fileItem = Object.prototype.toString.call(files[key]) === '[object Array]' ? files[key][0] : files[key];
      if (fileItem.mimetype.indexOf('image/') >= 0) { // 图片类型
        let filepath = fileItem.filepath;
        const extname = path.extname(filepath);
        // 没有后缀名，增加后缀名
        if (!extname && fileItem.mimetype.indexOf('/png;') >= 0) {
          filepath = filepath + '.png';
          fs.renameSync(fileItem.filepath, filepath);
        }
        // 生成缩略图
        const thumbPath = path.join(thumbFolder, path.basename(filepath))
        sharp(filepath)
          .resize(400, 400, {
            fit: sharp.fit.inside,
            withoutEnlargement: true
          })
          .toFile(thumbPath, (err) => {
            err && console.log(err);
          });
        images.push({
          filename: key,
          url: `/uploads/shop/${YYYYMM}/` + path.basename(filepath),
          thumb: `/uploads/shop/${YYYYMM}/thumb/` + path.basename(filepath),
          size: fileItem.size
        });
      }
    }

    res.json({
      result: 'ok',
      images
    });
  });
};

// 取子分类
export const subCatalog = async (req, res) => {
  const { id } = req.query;
  // 递归取子分类
  const [rows] = await mysql.query(`
    WITH RECURSIVE Subtree AS (
        -- 选择根节点
        SELECT
            id,
            replace(bin_to_uuid(uid, 1), "-", "") uid,
            parent_id,
            CONCAT(catalog_code, ' ', catalog_name) AS label,
            catalog_code,
            catalog_name,
            0 AS levels,
            sort
        FROM t_material_catalog
        WHERE parent_id>-1 AND uid = ? -- 替换为你要查询的根节点的 id
        UNION ALL
        -- 递归选择子节点
        SELECT
            c.id,
            replace(bin_to_uuid(c.uid, 1), "-", "") uid,
            c.parent_id,
            CONCAT(c.catalog_code, ' ', c.catalog_name) AS label,
            c.catalog_code,
            c.catalog_name,
            s.levels + 1 AS levels,
            c.sort
        FROM t_material_catalog c
        INNER JOIN Subtree s ON c.parent_id = s.id
        WHERE c.sell=1
    )
    SELECT
        id,
        uid,
        (SELECT replace(bin_to_uuid(uid, 1), "-", "") FROM t_material_catalog a WHERE a.id=Subtree.parent_id) parent_uid,
        parent_id,
        label,
        catalog_code,
        catalog_name,
        levels
    FROM Subtree
    WHERE levels>0
    ORDER BY levels, sort;`, [tools.uuidToBin(id)]);
  res.json(rows);
};
